package com.study.service.impl;

import com.study.constant.CommonKeys;
import com.study.entity.LicenceEntity;
import com.study.service.LicenceService;
import com.study.util.LicenceJsonUtil;
import com.study.util.NativeSecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * @author CSDN 流放深圳
 * @description 证书生成核心实现类
 * @create 2024-04-13 15:55
 * @since 1.0.0
 */
@Service
public class LicenceServiceImpl implements LicenceService {

    private static Logger log = LoggerFactory.getLogger(LicenceServiceImpl.class);

    /**
     * 获取配置文件的私钥
     */
    @Value("${licence.privateKey}")
    private String privateKey;

    /**
     * 获取配置文件的公钥
     */
    @Value("${licence.publicKey}")
    private String publicKey;


    /**
     * 创建证书 licence 内容
     * @param dtoEntity
     * @return
     */
    @Override
    public LicenceEntity createLicence(LicenceEntity dtoEntity) {
        LicenceEntity entity = new LicenceEntity();
        //赋值相同属性
        BeanUtils.copyProperties(dtoEntity, entity);
        //content 和 key 需要额外处理
        if(StringUtils.isEmpty(privateKey) || StringUtils.isEmpty(publicKey)) return null;
        entity.setKey(publicKey);//把公钥放进去，否则客户端无法获取公钥，就无法解密
        //把实体转成字符串
        String json = LicenceJsonUtil.objectToStr(entity);
        //把整个字符串加密
        String content = NativeSecurityUtil.encryptByPrivateKey(json, privateKey);
        //把加密后的字符串赋值给 content
        entity.setContent(content);
        return entity;
    }

    /**
     * 下载证书文件
     * @param dtoEntity
     * @param response
     */
    @Override
    public void downLoadLicence(LicenceEntity dtoEntity, HttpServletResponse response) {
        LicenceEntity licenceEntity = createLicence(dtoEntity);
        if(null == licenceEntity){
            log.error("证书的秘钥未配置！");
            return;
        }
        //把实体转为字符串
        String result = LicenceJsonUtil.objectToStr(licenceEntity);
        BufferedOutputStream out = null;
        try {
            //证书文件名
            String fileName = CommonKeys.CERTIFICATE_FILE;
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setCharacterEncoding("UTF-8");
            out = new BufferedOutputStream(response.getOutputStream());
            out.write(result.getBytes("UTF-8"));
            out.flush();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }

        //TODO 另外可以保存一份到指定目录下，方便测试（这是使用异步的形式）。上线的时候记得注释掉
        saveLicenceToCertificate(result);

    }

    /**
     * 异步保存证书文件到指定目录
     * @param result
     */
    @Async
    public void saveLicenceToCertificate(String result){
        try{
            // 创建证书目录（如果尚未创建）
            Path directory = Paths.get(CommonKeys.CERTIFICATE_DIRECTORY);
            Files.createDirectories(directory);
            // 构建证书文件的完整路径
            Path filePath = directory.resolve(CommonKeys.CERTIFICATE_FILE);
            // 检查文件是否存在，存在则删掉
            Files.deleteIfExists(filePath);
            //创建文件（如果文件已经存在，此步骤可能会抛出 FileAlreadyExistsException）
            Files.createFile(filePath);
            //写入内容到文件，使用 StandardOpenOption.APPEND 可以追加内容而不是覆盖
            Files.write(filePath, result.getBytes("UTF-8"), StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
        }catch (Exception e){
            log.error(e.getMessage(), e);
        }
    }

}
